/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <bits/alltypes.h>
#include <cstdint>
#include <string>
#include <cstdio>
#include <stack>

#include <vector>

#include <ace/xcomponent/native_interface_xcomponent.h>
#include <arkui/native_interface.h>
#include <arkui/native_node.h>
#include <arkui/native_type.h>
#include <hilog/log.h>

#include "manager.h"


namespace NativeXComponentSample {

    struct UserCallback {
        std::function<void(void *event)> callback;
    };

    class ArkUINativeModule {
    public:
        ArkUINativeModule() {
            nodeApi_ = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(OH_ArkUI_GetNativeAPI(ARKUI_NATIVE_NODE, 1));
            // 统一入口函数处理事件。
            nodeApi_->registerNodeCustomEventReceiver([](ArkUI_NodeCustomEvent *event) {
                auto *userData = reinterpret_cast<UserCallback *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
                userData->callback(event);
            });
        }

        ~ArkUINativeModule() { nodeApi_->unregisterNodeCustomEventReceiver(); }

        static ArkUINativeModule *GetInstance() {
            static ArkUINativeModule nativeModule;
            return &nativeModule;
        }

        ArkUI_NativeNodeAPI_1 *GetNodeAPI() { return nodeApi_; }

    private:
        ArkUI_NativeNodeAPI_1 *nodeApi_ = nullptr;
    };

    class MyList {
    public:
        MyList() {
            nodeApi_ = ArkUINativeModule::GetInstance()->GetNodeAPI();
            // 初始化数据
            for (int32_t i = 0; i < 1000; i++) {
                data_.emplace_back(std::to_string(i));
            }
            adapter_ = OH_ArkUI_NodeAdapter_Create();
            // 设置节点总数为1000。
            OH_ArkUI_NodeAdapter_SetTotalNodeCount(adapter_, 1000);

            // 设置组件创建、销毁回调。
            adapterCallback_ = new UserCallback();
            adapterCallback_->callback = [this](void *event) {
                auto *adapterEvent = reinterpret_cast<ArkUI_NodeAdapterEvent *>(event);
                auto type = OH_ArkUI_NodeAdapterEvent_GetType(adapterEvent);
                switch (type) {
                case NODE_ADAPTER_EVENT_ON_ATTACH_TO_NODE:
                    OnAttachToNode(adapterEvent);
                    break;
                case NODE_ADAPTER_EVENT_ON_DETACH_FROM_NODE:
                    OnDetachFromNode(adapterEvent);
                    break;
                case NODE_ADAPTER_EVENT_ON_GET_NODE_ID:
                    OnGetChildId(adapterEvent);
                    break;
                case NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER:
                    OnCreateNewChild(adapterEvent);
                    break;
                case NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER:
                    OnDisposeChild(adapterEvent);
                    break;
                }
            };
            OH_ArkUI_NodeAdapter_RegisterEventReceiver(adapter_, adapterCallback_, [](ArkUI_NodeAdapterEvent *event) {
                auto *userData = reinterpret_cast<UserCallback *>(OH_ArkUI_NodeAdapterEvent_GetUserData(event));
                userData->callback(event);
            });
            // 创建List并设置Adapter和缓存数量。
            list_ = nodeApi_->createNode(ARKUI_NODE_LIST);
            ArkUI_AttributeItem item{nullptr, 0, nullptr, adapter_};
            nodeApi_->setAttribute(list_, NODE_LIST_NODE_ADAPTER, &item);
            ArkUI_NumberValue value[1] = {{.i32 = 10}};
            ArkUI_AttributeItem cacheSize{value, 1};
            nodeApi_->setAttribute(list_, NODE_LIST_CACHED_COUNT, &cacheSize);
            value[0] = {300};
            ArkUI_AttributeItem width{value, 1};
            nodeApi_->setAttribute(list_, NODE_WIDTH, &width);
            value[0] = {600};
            ArkUI_AttributeItem height{value, 1};
            nodeApi_->setAttribute(list_, NODE_HEIGHT, &height);
        }

        ~MyList() {
            nodeApi_->disposeNode(list_);
            // 销毁Adapter中所有组件。
            ArkUI_NodeHandle *items = nullptr;
            uint32_t size = 0;
            OH_ArkUI_NodeAdapter_GetAllItem(adapter_, &items, &size);
            for (uint32_t i = 0; i < size; i++) {
                auto *listItem = items[i];
                auto *text = nodeApi_->getFirstChild(listItem);
                nodeApi_->disposeNode(text);
                nodeApi_->disposeNode(listItem);
            }
            // 销毁adapter
            OH_ArkUI_NodeAdapter_Dispose(adapter_);
            // 销毁缓存组件。
            while (!nodePool_.empty()) {
                auto *listItem = nodePool_.top();
                auto *text = nodeApi_->getFirstChild(listItem);
                nodeApi_->disposeNode(listItem);
                nodeApi_->disposeNode(text);
                nodePool_.pop();
            }
            delete adapterCallback_;
        }

        void RemoveData() {
            // 删除最后一个数据
            data_.erase(data_.begin());
            // 由于index从0开始计算，删除的位置为999。
            // 如果第999个元素保存在NodeAdapter中，那么会将元素删除并回调NODE_ADAPTER_EVENT_ON_DISPOSE_NODE事件通知开发者释放节点资源。
            // 如果第999个元素不在NodeAdapter中，不会进行任何操作和回调通知。
            OH_ArkUI_NodeAdapter_NotifyItemRemoved(adapter_, 0, 1);
        }

        void InsertData() {
            // 新增一个数据
            data_.insert(data_.begin(), "new insert data");
            // 由于index从0开始计算，故插入的位置就在999。
            // 如果第999个元素位于父组件可视区域中进行展示，那么会回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID事件获取组件标识符，
            // 随后回调NODE_ADAPTER_EVENT_ON_CREATE_NODE创建节点并保持在NodeAdapter中。
            // 如果第999个元素不在展示范围中，不会进行任何操作和回调通知。
            OH_ArkUI_NodeAdapter_NotifyItemInserted(adapter_, 0, 1);
        }

        void ChangeData() {
            data_[0] = "new change data";
            OH_ArkUI_NodeAdapter_NotifyItemChanged(adapter_, 0, 1);
        }

        void MoveData() {
            auto temp1 = data_[0];
            auto temp2 = data_[1];
            data_[1] = data_[2];
            data_[2] = temp1;
            data_[0] = temp2;
            // 移动位置不会产生回调。
            OH_ArkUI_NodeAdapter_NotifyItemMoved(adapter_, 0, 2);
        }

        void ReloadData() {
            std::reverse(data_.begin(), data_.end());
            // 框架会基于当前可视范围内的所有元素，重新调用NODE_ADAPTER_EVENT_ON_GET_NODE_ID事件获取组件标识符；
            // 基于新的标识符和老的标识符进行比较，复用标识符相同的元素；
            // 针对新的标识符调用NODE_ADAPTER_EVENT_ON_CREATE_NODE创建节点并保持在NodeAdapter中；
            // 针对需要删除的标识符调用NODE_ADAPTER_EVENT_ON_DISPOSE_NODE事件通知开发者释放节点资源。
            OH_ArkUI_NodeAdapter_NotifyItemReloaded(adapter_);
        }

        ArkUI_NodeHandle GetMyList() { return list_; }

    protected:
        void OnAttachToNode(ArkUI_NodeAdapterEvent *event) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Adapter attach to node");
        }

        void OnDetachFromNode(ArkUI_NodeAdapterEvent *event) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Adapter detach from node");
        }

        void OnGetChildId(ArkUI_NodeAdapterEvent *event) {
            auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
            // 设置组件唯一标识符。
            auto hash = std::hash<std::string>();
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "OnGetChildId %{public}d, %lu", index,
                         hash(data_[index]));
            OH_ArkUI_NodeAdapterEvent_SetNodeId(event, hash(data_[index]));
        }

        void OnCreateNewChild(ArkUI_NodeAdapterEvent *event) {
            auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "OnCreateNewChild %{public}d", index);
            ArkUI_NodeHandle listItem = nullptr;
            if (!nodePool_.empty()) {
                // 复用缓存
                listItem = nodePool_.top();
                nodePool_.pop();
                // 更新数据
                auto *text = nodeApi_->getFirstChild(listItem);
                ArkUI_AttributeItem item{nullptr, 0, data_[index].c_str()};
                nodeApi_->setAttribute(text, NODE_TEXT_CONTENT, &item);
            } else {
                // 重新创建。
                auto *text = nodeApi_->createNode(ARKUI_NODE_TEXT);
                ArkUI_AttributeItem item{nullptr, 0, data_[index].c_str()};
                nodeApi_->setAttribute(text, NODE_TEXT_CONTENT, &item);
                listItem = nodeApi_->createNode(ARKUI_NODE_LIST_ITEM);
                ArkUI_NumberValue value[] = {20};
                ArkUI_AttributeItem height{value, 1};
                nodeApi_->setAttribute(listItem, NODE_HEIGHT, &height);
                value[0] = {200};
                ArkUI_AttributeItem width{value, 1};
                nodeApi_->setAttribute(listItem, NODE_WIDTH, &width);
                value[0] = {.u32 = 0xFF112233};
                ArkUI_AttributeItem backgroundColor{value, 1};
                nodeApi_->setAttribute(listItem, NODE_BACKGROUND_COLOR, &backgroundColor);
                nodeApi_->addChild(listItem, text);
            }
            OH_ArkUI_NodeAdapterEvent_SetAddedItem(event, listItem);
        }

        void OnDisposeChild(ArkUI_NodeAdapterEvent *event) {
            auto *node = OH_ArkUI_NodeAdapterEvent_GetRemovedNode(event);
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "OnDisposeChild: %{public}p", node);
            // 缓存节点
            nodePool_.emplace(node);
        }

    private:
        std::vector<std::string> data_;
        ArkUI_NativeNodeAPI_1 *nodeApi_ = nullptr;
        ArkUI_NodeHandle list_ = nullptr;
        ArkUI_NodeAdapterHandle adapter_ = nullptr;
        UserCallback *adapterCallback_ = nullptr;
        // 节点缓存池，用于回收复用。
        std::stack<ArkUI_NodeHandle> nodePool_;
    };

    typedef struct ArkUIArenaHandle__ *ArkUIArenaHandle;

    ArkUIArenaHandle CreateArkUIArenaHandle(int32_t minSize, int32_t maxSize) { return nullptr; };

    void (*freeStringArena)(ArkUIArenaHandle arena){};

    const char *requestInStringArena(ArkUIArenaHandle handle, int32_t neededSize) { return nullptr; };

    Manager Manager::manager_;

    Manager::~Manager() {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "~Manager");
        for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) {
            if (iter->second != nullptr) {
                iter->second = nullptr;
            }
        }
        nativeXComponentMap_.clear();

        for (auto iter = containerMap_.begin(); iter != containerMap_.end(); ++iter) {
            if (iter->second != nullptr) {
                delete iter->second;
                iter->second = nullptr;
            }
        }
        containerMap_.clear();
    }

    MyList *g_list = nullptr;
    napi_value Manager::CreateNativeNode(napi_env env, napi_callback_info info) {
        if ((env == nullptr) || (info == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode env or info is null");
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  1111");

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_string) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        size_t length;
        if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  2222 %{public}s", idStr);

        auto manager = Manager::GetInstance();
        if (manager == nullptr) {
            return nullptr;
        }

        OH_NativeXComponent *component = manager->GetNativeXComponent(idStr);
        if (component == nullptr) {
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  3333333");

        g_list = new MyList();
        OH_NativeXComponent_AttachNativeRootNode(component, g_list->GetMyList());

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  555");
        return nullptr;
    }

    napi_value Manager::ChangeData(napi_env env, napi_callback_info info) {
        g_list->ChangeData();
        return nullptr;
    }
    napi_value Manager::InsertData(napi_env env, napi_callback_info info) {
        g_list->InsertData();
        return nullptr;
    }
    napi_value Manager::RemoveData(napi_env env, napi_callback_info info) {
        g_list->RemoveData();
        return nullptr;
    }
    napi_value Manager::MoveData(napi_env env, napi_callback_info info) {
        g_list->MoveData();
        return nullptr;
    }
    napi_value Manager::ReloadData(napi_env env, napi_callback_info info) {
        g_list->ReloadData();
        return nullptr;
    }

    napi_value Manager::UpdateNativeNode(napi_env env, napi_callback_info info) {

        if ((env == nullptr) || (info == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode env or info is null");
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  1111");

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_string) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        size_t length;
        if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "CreateNativeNode  1111 %{public}s", idStr);

        auto manager = Manager::GetInstance();
        if (manager == nullptr) {
            return nullptr;
        }

        OH_NativeXComponent *component = manager->GetNativeXComponent(idStr);
        if (component == nullptr) {
            return nullptr;
        }


        if ((env == nullptr) || (info == nullptr || component == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext env or info is null");
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  1111");
        //         OH_NativeNode *nodeAPI;
        //         OH_NativeXComponent_GetNativeNode(component, &nodeAPI);
        //         if (nodeAPI != nullptr) {
        //             OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  222");
        //
        //             if (nodeAPI->CreateNode != nullptr && nodeAPI->AddChild != nullptr && nodeAPI->GetCommonModifier)
        //             {
        //                 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  222--1");
        //
        //                 const OH_CommonModifier *modifier = nodeAPI->GetCommonModifier();
        //
        //                 OH_NativeNodeHandle text1 = nodeAPI->GetNodeById(100);
        //                 OH_NativeNodeHandle text2 = nodeAPI->GetNodeById(102);
        //                 OH_NativeNodeHandle text3 = nodeAPI->GetNodeById(104);
        //                 OH_NativeNodeHandle stack = nodeAPI->GetNodeById(105);
        //
        //                 modifier->SetWidth(text2, 0.7, 3, nullptr);   // 100 percent
        //                 modifier->SetHeight(text2, 55, 0, nullptr); // px
        //                 modifier->SetBackgroundColor(text2, 0xff00ff00);
        //
        //                 nodeAPI->RemoveChild(stack, text1);
        //                 nodeAPI->RemoveChild(stack, text3);
        //                 OH_NativeXComponent_MarkDirtyContainer(component, 1 << 9);
        //             }
        //         }
        //         OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  333");
        return nullptr;
    }


    napi_value Manager::GetContext(napi_env env, napi_callback_info info) {
        if ((env == nullptr) || (info == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext env or info is null");
            return nullptr;
        }

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_number) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        int64_t value;
        if (napi_get_value_int64(env, args[0], &value) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }

        napi_value exports;
        if (napi_create_object(env, &exports) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_create_object failed");
            return nullptr;
        }

        return exports;
    }

    void Manager::Export(napi_env env, napi_value exports) {
        if ((env == nullptr) || (exports == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: env or exports is null");
            return;
        }

        napi_value exportInstance = nullptr;
        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_get_named_property fail");
            return;
        }

        OH_NativeXComponent *nativeXComponent = nullptr;
        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_unwrap fail");
            return;
        }

        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) !=
            OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager",
                         "Export: OH_NativeXComponent_GetXComponentId fail");
            return;
        }

        std::string id(idStr);
        auto manager = Manager::GetInstance();
        if ((manager != nullptr) && (nativeXComponent != nullptr)) {
            manager->SetNativeXComponent(id, nativeXComponent);
            auto container = manager->GetContainer(id);
            if (container != nullptr) {
                container->RegisterCallback(nativeXComponent);
                //            container->Export(env, exports);
            }
        }
    }

    void Manager::SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent) {
        if (nativeXComponent == nullptr) {
            return;
        }

        if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) {
            nativeXComponentMap_[id] = nativeXComponent;
            return;
        }

        if (nativeXComponentMap_[id] != nativeXComponent) {
            OH_NativeXComponent *tmp = nativeXComponentMap_[id];
            delete tmp;
            tmp = nullptr;
            nativeXComponentMap_[id] = nativeXComponent;
        }
    }

    OH_NativeXComponent *Manager::GetNativeXComponent(const std::string &id) { return nativeXComponentMap_[id]; }

    Container *Manager::GetContainer(std::string &id) {
        if (containerMap_.find(id) == containerMap_.end()) {
            Container *instance = Container::GetInstance(id);
            containerMap_[id] = instance;
            return instance;
        }

        return containerMap_[id];
    }
} // namespace NativeXComponentSample
